package main

import (
	"crypto/md5"
	"fmt"
	"html/template"
	"io"
	"log"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strconv"
	"time"
)

var (
	token string
)

func sayhelloName(w http.ResponseWriter, r *http.Request) {
	//r.ParseForm() //解析url传递的参数，对于POST则解析响应包的主体（request body）
	////注意:如果没有调用ParseForm方法，下面无法获取表单的数据
	//fmt.Println(r.Form) //这些信息是输出到服务器端的打印信息
	//fmt.Println("path", r.URL.Path)
	//fmt.Println("scheme", r.URL.Scheme)
	//fmt.Println(r.Form["url_long"])
	//for k, v := range r.Form {
	//	fmt.Println("key:", k)
	//	fmt.Println("val:", strings.Join(v, ""))
	//}
	//fmt.Fprintf(w, "Hello astaxie!") //这个写入到w的是输出到客户端的
}

func login(w http.ResponseWriter, r *http.Request) {
	fmt.Println("method:", r.Method) //获取请求的方法
	if r.Method == "GET" {
		gtplfile, _ := filepath.Abs("login.gtpl")
		t, _ := template.ParseFiles(gtplfile)
		t.Execute(w, nil)
	} else {
		fmt.Printf("\nPost request..\n")

		//r.Form的使用示例
		fmt.Printf("\n Example ---> Use of r.Form <---\n")
		//r.Form包含了get和post参数，并且先解析post参数的参数.
		//Form：存储了post、put和get参数，在使用之前需要调用ParseForm方法
		r.ParseForm()
		fmt.Println("username:", r.Form["username"][0], r.Form["username"][1])
		fmt.Println("password:", r.Form["password"][0], r.Form["password"][1])

		//url.ParseQuery 使用示例
		fmt.Printf("\n Example ---> Use of url.ParseQuery <---\n")
		//分析url问号后的参数,也就是解析Get参数。事实上这个也是标准库ParseForm中关于get参数解析代码。
		queryForm, err := url.ParseQuery(r.URL.RawQuery)
		if err == nil && len(queryForm["username"]) > 0 {

			for i := 0; i < len(queryForm["username"]); i++ {
				fmt.Printf("name:%s\n", queryForm["username"][i])
			}
		}
	}
}

func postexample(w http.ResponseWriter, r *http.Request) {
	fmt.Println("method:", r.Method) //获取请求的方法
	if r.Method == "GET" {
		gtplfile, _ := filepath.Abs("postexample.gtpl")
		t, _ := template.ParseFiles(gtplfile)
		t.Execute(w, nil)
	} else {
		fmt.Printf("\nPost request..\n")

		//以下两个示例都假设http的表单请求的enctype的属性为application/x-www-form-urlencoded
		//r.PostFormValue 使用示例
		fmt.Printf("\nExample ---> Use of r.PostFormValue <---\n")
		//r.PostFormValue 存储了post、put参数,在使用之前不需要调用ParseForm方法。
		//r.PostFormValue 只能获得第一个匹配key的值
		username := r.PostFormValue("username")
		fmt.Println("username:", username)

		//r.PostForm 使用示例
		fmt.Printf("\nExample ---> Use of r.PostForm <---\n")
		//PostForm：存储了post、put参数，在使用之前需要调用ParseForm方法。
		r.ParseForm()
		username2 := r.PostForm["username"]

		for k, v := range username2 {
			fmt.Println("index:", k, "value:", v)
		}

	}
}

func pathExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

func upfile(w http.ResponseWriter, r *http.Request) {
	fmt.Println("method:", r.Method) //获取请求的方法
	if r.Method == "GET" {
		gtplfile, _ := filepath.Abs("fileupload.gtpl")
		t, _ := template.ParseFiles(gtplfile)
		t.Execute(w, nil)
	} else {
		//r.MultipartForm 使用示例
		fmt.Printf("\nExample ---> Use of r.ParseMultipartForm <---\n")
		//1.MultipartForm：存储了包含了文件上传的表单的post参数，在使用前需要调用ParseMultipartForm方法
		//2.因为是文件上传,表单请求的enctype的属性为multipart/form-data
		//3.r.MultipartForm.Value["username"] 的值是post中的值
		r.ParseMultipartForm(32 << 20) //32M
		if r.MultipartForm != nil {
			values := r.MultipartForm.Value["username"]

			for k, v := range values {
				fmt.Println("index:", k, "value:", v)
			}

			if len(values) > 0 {
				fmt.Fprintln(w, values)
			}

			file, handler, err := r.FormFile("uploadfile")
			if err != nil {
				fmt.Println(err)
				return
			}
			defer file.Close()

			var dir string
			//目录不存在新创建目录,存在则使用存在的目录
			bool, _ := pathExists("fileupload")
			if !bool {
				dirbase, _ := os.Getwd()
				dir = fmt.Sprintf("%s%c%s%c", dirbase, os.PathSeparator, "fileupload", os.PathSeparator)
				fmt.Println("dir:", dir)
			} else {
				dir = "fileupload"
			}
			//创建fileupload目录
			os.Mkdir(dir, os.ModePerm)
			f, err := os.OpenFile(dir+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)
			if err != nil {
				fmt.Println(err)
				return
			}
			defer f.Close()
			io.Copy(f, file)
		}
	}
}

//这个路由接口讲解 如何防止多次递交表单
func mutlisubmit(w http.ResponseWriter, r *http.Request) {
	fmt.Println("method:", r.Method) //获取请求的方法
	if r.Method == "GET" {
		crutime := time.Now().Unix()
		h := md5.New()
		io.WriteString(h, strconv.FormatInt(crutime, 10))
		token := fmt.Sprintf("%x", h.Sum(nil))
		gtplfile, _ := filepath.Abs("mutlisubmit.gtpl")
		t, _ := template.ParseFiles(gtplfile)
		t.Execute(w, token)
	} else {
		fmt.Println("Post in:") //获取请求的方法
		//请求的是登陆数据，那么执行登陆的逻辑判断
		r.ParseForm()

		//通过在模版里面增加了一个隐藏字段token，这个值我们通过MD5(时间戳)来获取惟一值，然后我们把这个值存储到服务器端
		t := r.Form.Get("token")
		fmt.Println("t:", t)
		if t != "" && t == token {
			//验证token的合法性
			fmt.Println("token is vaild")
		} else {
			//不存在token报错
			fmt.Println("token is invaild")
			return
		}

		fmt.Println("username length:", len(r.Form["username"][0]))
		fmt.Println("username:", template.HTMLEscapeString(r.Form.Get("username"))) //输出到服务器端
		fmt.Println("password:", template.HTMLEscapeString(r.Form.Get("password")))
		template.HTMLEscape(w, []byte(r.Form.Get("username"))) //输出到客户端
	}
}

//MultipartForm,PostForm,PostFormValue,ParseQuery的区别
//1.MultipartForm处理文件上传,针对 表单enctype的属性为multipart/form-data的请求
//2.PostForm 获取post,put请求中同一个key的多个value
//3.PostFormValue 只能获取post,put请求中同一个key的第一个value
//4.ParseQuery解析在post请求中的get参数

//讲解go处理表单的输入
func main() {
	http.HandleFunc("/", sayhelloName)           //设置访问的路由
	http.HandleFunc("/login", login)             //设置访问的路由
	http.HandleFunc("/postexample", postexample) //设置访问的路由
	http.HandleFunc("/upfile", upfile)           //文件上传示例
	http.HandleFunc("/mutlisub", mutlisubmit)    //防止多次递交表单 代码示例

	err := http.ListenAndServe(":9090", nil) //设置监听的端口
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
